﻿using System;
using System.Collections.Generic;
using System.Reflection;
using ServerFramework.Net.Msg;
using ServerFramework.Net;
using ServerFramework.Tools;
using System.Threading;
using System.Collections.Concurrent;

namespace ServerFramework.Net.Tcp
{
    /*rpc 调用*/
    [Serializable]
    //rpc 协议 调用协议
    public class RpcRequst : BinaryMsg
    {
        public string rpcUID;
        /// <summary>
        /// 函数名
        /// </summary>
        public string methodName = "";
        /// <summary>
        /// 参数
        /// </summary>
        public object[] args;    //所有参数

        protected override void WriteMsgId()
        {
            _msgId = SystemMsgID.rpcCallId;
        }
    }

    [Serializable]
    /// <summary>
    /// 返回值
    /// </summary>
    public class RpcReturnVal : BinaryMsg
    {
        public string rpcUID;
        public object returnVal;
        protected override void WriteMsgId()
        {
            _msgId = SystemMsgID.rpcReturnId;
        }
        public T ToObject<T>()
        {
            return (T)returnVal;
        }

    }

    /// <summary>
    /// rpc 属性标记
    /// </summary>
    public class RpcAttribute : Attribute
    {

    }



    /// <summary>
    /// rpc调用
    /// </summary>
    public abstract class Rpc
    {
        protected Dictionary<string, MethodInfo> _rpcMethodCache = new Dictionary<string, MethodInfo>();
        protected SafeDictionary<string, RpcReturnInfo> _returnVals = new SafeDictionary<string, RpcReturnInfo>();

        public RpcReturnInfo ReturnCall 
        {
            get 
            {
                return new RpcReturnInfo(Guid.NewGuid().ToString(), this);
            }
        }
        /// <summary>
        /// 返回值信息
        /// </summary>
        public class RpcReturnInfo
        {
            public string RpcGuid { get; private set; }
            protected AutoResetEvent _resetEvent = null;
            protected object _returnValue = null;

            protected Rpc _rpc;

            public RpcReturnInfo(string guid,Rpc rpc)
            {
                RpcGuid = guid;
                _rpc = rpc;
               
            }
            public object Call(string methodName, params object[] args)
            {
                _resetEvent = new AutoResetEvent(false);
                _rpc._returnVals.Add(RpcGuid, this);
                _rpc.RpcReturnCall(RpcGuid, methodName, args);
                _resetEvent.WaitOne(1000*60);
                _resetEvent.Close();
                _rpc._returnVals.Remove(RpcGuid);
                return _returnValue;
            }

            public object Call(ISender sender, string methodName, params object[] args)
            {
                _resetEvent = new AutoResetEvent(false);
                _rpc._returnVals.Add(RpcGuid, this);
                _rpc.RpcReturnCall(RpcGuid, sender, methodName, args);
                _resetEvent.WaitOne(1000 * 60);
                _resetEvent.Close();
                _rpc._returnVals.Remove(RpcGuid);
                return _returnValue;
            }

            public void Return(object returnVal)
            {
                _returnValue = returnVal;
                _resetEvent.Set();
            }
         
        }
        /// <summary>
        /// 获取调用id
        /// </summary>
        public Rpc()
        {
            var methods = this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (var m in methods)
            {
                var rpcAttr = Attribute.GetCustomAttribute(m, typeof(RpcAttribute));
                if (rpcAttr != null)
                {
                    _rpcMethodCache.Add(m.Name, m);
                }
            }
            _returnVals.Clear();
        }

        /// <summary>
        /// rpc 调用
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="callback"></param>
        /// <param name="args"></param>
        public void RpcCall(ISender sender, string methodName, params object[] args)
        {
            var send = new RpcRequst();
            send.methodName = methodName;
            send.args = args;
            RpcSend(sender, send);
        }

        /// <summary>
        ///返回值的rpc调用
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="sender"></param>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        protected void RpcReturnCall(string guid,ISender sender, string methodName, params object[] args)
        {
            var send = new RpcRequst();
            send.rpcUID = guid;
            send.methodName = methodName;
            send.args = args;
            RpcSend(sender, send);
        }

        protected void RpcReturnCall(string guid, string methodName, params object[] args)
        {
            var send = new RpcRequst();
            send.rpcUID = guid;
            send.methodName = methodName;
            send.args = args;
            RpcSend(null, send);
        }


        /// <summary>
        /// rpc 调用 客户端使用
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        public void RpcCall(string methodName, params object[] args)
        {
            var send = new RpcRequst();
            send.methodName = methodName;
            send.args = args;
            RpcSend(null, send);
        }



        protected virtual void RpcSend(ISender sender, RpcRequst requst)
        {

        }


        /// <summary>
        /// rpc 收到消息
        /// </summary>
        /// <param name="packet"></param>
        protected void RpcRecv(MsgPacket packet)
        {
            ISender sender = packet.userToken as ISender;
            if (sender == null)
            {
                return;
            }
            if (packet.msgId == SystemMsgID.rpcCallId)
            {
                var rpcMsg = RpcRequst.Unpack<RpcRequst>(packet);
                if (rpcMsg != null)
                {
                    MethodInfo method = null;
                    if (_rpcMethodCache.TryGetValue(rpcMsg.methodName, out method))
                    {
                        List<object> parametes = new List<object>();
                        parametes.Add(sender);
                        parametes.AddRange(rpcMsg.args);
                        var methodParametes = method.GetParameters();
                        if(parametes.Count != methodParametes.Length) 
                        {
                            Log.WriteLog(ELogLevel.LOG_ERROR, "paramtes count not same, methodName = " + method.Name);
                            return;
                        }
                        object returnVal = method.Invoke(this, parametes.ToArray());
                        if (returnVal != null && !string.IsNullOrEmpty(rpcMsg.rpcUID))
                        {
                            var returnMsg = new RpcReturnVal();
                            returnMsg.rpcUID = rpcMsg.rpcUID;
                            returnMsg.returnVal = returnVal;
                            sender.Send(returnMsg.Pack());
                        }
                    }
                }

            }
            else if (packet.msgId == SystemMsgID.rpcReturnId)
            {
                RpcReturnVal returnVal = RpcReturnVal.Unpack<RpcReturnVal>(packet);
                if (returnVal == null)
                {
                    return;
                }
                RpcReturnInfo ret = null;
                if (_returnVals.TryGetValue(returnVal.rpcUID, out ret))
                {
                    ret.Return(returnVal.returnVal);
                }
            }
            else
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, "未知 msgId：" + packet.msgId);
            }
        }
    }
}
